// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat.logic.chat_root.sendimg;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.eva.android.BitmapHelper;
import com.eva.android.UriToFileHelper;
import com.eva.android.widget.ImageViewActivity;
import com.eva.android.widget.WidgetUtils;
import com.eva.android.widget.WidgetUtils.ToastType;
import com.eva.epc.common.file.FileHelper;
import com.x52im.rbchat.Const;
import com.x52im.rbchat.R;
import com.x52im.rbchat.logic.chat_friend.utils.ChatDataHelper;
import com.x52im.rbchat.logic.chat_friend.utils.MessageHelper;
import com.x52im.rbchat.logic.chat_group.utils.GChatDataHelper;
import com.x52im.rbchat.logic.chat_group.utils.GMessageHelper;
import com.x52im.rbchat.logic.chat_guest.utils.TChatDataHelper;
import com.x52im.rbchat.logic.chat_guest.utils.TMessageHelper;
import com.x52im.rbchat.logic.chat_root.model.Message;
import com.x52im.rbchat.logic.chat_root.model.MessageExt;

import net.x52im.mobileimsdk.server.protocal.Protocal;

import java.io.File;
import java.util.Observable;
import java.util.Observer;

/**
 * 要发送图片消息的图片预览和发送处理Activity实现类.
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @since 2.2
 */
public class PreviewAndSendActivity extends ImageViewActivity
{
//	/** 功能应用场景：for 好友的正式聊天 */
//	public final static String CHAT_TYPE_FREIDN$CHAT = String.valueOf(MsgBodyRoot.CHAT_TYPE_FREIDN$CHAT);
//	/** 功能应用场景：for 陌生人聊天（临时聊天） */
//	public final static String CHAT_TYPE_GUEST$CHAT = String.valueOf(MsgBodyRoot.CHAT_TYPE_GUEST$CHAT);
//	/** 功能应用场景：for 世界频道或普通群聊 */
//	public final static String CHAT_TYPE_GROUP$CHAT = String.valueOf(MsgBodyRoot.CHAT_TYPE_GROUP$CHAT);
	
	private final static String TAG = PreviewAndSendActivity.class.getSimpleName();
	
//	/** 压缩尺寸：当图片尺寸过大时，发送前要压缩的尺寸比例 */
//	// 在MeiZu2上测试的结果：拍出1536*2048后(1.63M)，压缩尺寸（0.75f）且降质量75后的大小是132K
//	// 在MeiZu2上测试的结果：拍出1536*2048后(1.48M)，压缩尺寸（0.75f）后的大小是963.17K
//	private final float DECREASE_PERCENT = 0.75f;
	
	/** 压缩质量：发送前要压缩的图片质量（0~100值） */
	// 在魅族2上，微信先将图片缩小至75%后，再压缩质量（从400K左右到35K左右），经测试估计是质量75哦
	// 调整COMPRESS_QUALITY值可压缩图像大小，最大100. meizu2上100时400K左右，75时35K左右，再低则
	// 图像的大小变化不太明显了（20为13K左右）
	private final int COMPRESS_QUALITY = 75;
	
	/** 
	 * 经过压缩、重命名处理后真正要发送出去的文件名（此文件名可作为发送方的本地缓存直接使用）.
	 * 除非压缩、重命名等处理成功完成，否则本字段将一直是null. */
	private String processedFileName = null;
	
	/**
	 * 本方法由父类的onCreate()调用，子类可在此方法中实现自已的UI显示逻辑。
	 */
	@Override
	protected void initViews(Bundle savedInstanceState) {
		super.initViews(savedInstanceState);
		
		this.setTitle($$(R.string.chat_sendpic_preview_and_send_title));
		// 设置操作条可见
		this.getFunctionBarLayout().setVisibility(View.VISIBLE);
		this.getFunctionButton1().setVisibility(View.VISIBLE);
		this.getFunctionButton1().setText(getString(R.string.chat_sendpic_image_view_sendpic));
		// 设置保存图片按钮不可见（不然被半透明操作条挡住，也不好看）
        this.mBtnSavePicToGalery.setVisibility(View.GONE);
	}
	
	/**
	 * 点击操作按钮时要调用的方法.
	 * <p>
	 * 在本类中，此按钮的意义是“发送图片”。
	 */
	@Override
	protected void fireOpr()
	{
		// 文件压缩并重命名成功的观察者
		Observer notifyObserverAfterSucess = (observable, data) -> {
			// 将重命名后的文件名保存到全局变量保存
			processedFileName = (String)data;
		};

		// 开始压缩并重命名（用md5码作为文件名）
		boolean decreaseSizeOK = decreaseAndRenameSize(this
				, mImageDataType, mImageDataSrc
				, mBmOriginalForView, COMPRESS_QUALITY
				, notifyObserverAfterSucess);
		
		if(decreaseSizeOK) {
			// 根据start时的Intent传的参数，好友uid号就存放于exData1中
			String friendUid = mExData1;
			// 功能应用场景
			String usedFor = (String)mExData2;

			// 开始真正的图片发送完整逻辑
			PreviewAndSendActivity.doSendLogic(this, friendUid, usedFor, processedFileName);
		} else {
			WidgetUtils.showToast(this, $$(R.string.chat_sendpic_preview_and_send_compressfailed), ToastType.WARN);
		}
		
		// 完成后关闭本界面
		finish();
	}

	/**
	 * 开始真正的图片发送完整逻辑（加入一行数据到聊天列表模型中、并执行图片上传、指令发送等逻辑）。
	 *
	 * @param context
	 * @param to 要发送的对象（如果是一对一聊天则本参数是对方的uid，如果是世界频道或群聊则本参数为gid）
	 * @param usedFor 用途
	 * @param processedFileName 经过前置处理后的最终文件名（前置处理包括：图片裁剪、图片压缩、计算md5码、重命名等环节）
	 * @param sucessObsExtra 消息发送完成后，额外要做的事
	 */
	public static void doSendLogic(Context context, String to, String usedFor, String processedFileName) {
		doSendLogic(context,false, to, null, usedFor, processedFileName, null);
	}

    /**
     * 开始真正的图片发送完整逻辑（加入一行数据到聊天列表模型中、并执行图片上传、指令发送等逻辑）。
     *
     * @param context
     * @param to 要发送的对象（如果是一对一聊天则本参数是对方的uid，如果是世界频道或群聊则本参数为gid）
	 * @param toName 要发送的对象名（当前仅用于陌生人聊天时）
     * @param usedFor 用途
     * @param processedFileName 经过前置处理后的最终文件名（前置处理包括：图片裁剪、图片压缩、计算md5码、重命名等环节）
	 * @param sucessObsExtra 消息发送完成后，额外要做的事
     */
	public static void doSendLogic(Context context, boolean forForward, String to, String toName, String usedFor, String processedFileName, Observer sucessObsExtra) {
        // 先生成指纹码
        String fingerPring = Protocal.genFingerPrint();

		Message m = MessageExt.createChatMsgEntity_OUTGO_IMAGE(processedFileName, fingerPring);

        // 图片发送时先在界面上把消息显示出来（其实真正的消息还没有发送出去呢），目的在于：
        // 因为图片还要先上传到服务端，只在图片上传到服务端成功后才会真正地把图片消息发给
        // 好友。而且先把消息在本地界面中显示出来还可以在消息气泡里显示图片的上传进度，一举多得
        // 【注意】图片的上传和消息发送不在本Activity中完成，原因是：如果要等到图片上传和消息发送
        //        完成则用户可能就得在这个界面中先等待处理完成才能回到聊天界面，包括微信在内都不是
        //        这样处理的，这会影响用户体验（会让用户感觉发送图片有点慢）。当前的实现思路是无条件
        //        先加一条消息到本地界面，本地界面的消息列表会自动判断如果该图片消息的处理状态是”等待处理中“
        //        时才启动图片的上传和消息的发送过程（虽然技术繁琐很多，但至少用户体验跟微信等主流IM一样了！）

		// 如果是用于消息转发，需要直接跳过文件的处理过程（因为当转发的是收到的文件时，很可能这个文件还没下载过，
		// 而且转发的逻辑，本来就是只转发成功被发送的消息，所以不走文件处理流程完全合理）
		if(forForward) {
			m.setSendStatusSecondary(Message.SendStatusSecondary.none);
		}

		// 用于正式聊天的图片发送
        if(Const.CHAT_TYPE_FREIDN$CHAT.equals(usedFor)) {
			// 插入一条聊天数据到界面等
			ChatDataHelper.addChatMessageData_outgoing(context, to, m);

			// 如果是用于消息转发，就直接跳过文件的处理过程，直接发消息（因为转发功能的使用前提是被转消息是正常收发状态的消息，即文件已经上传处理好了的）
			if(forForward) {
				// 并发送一条图片消息给好友
				MessageHelper.sendImageMessageAsync((Activity)context, to, processedFileName, fingerPring, null);
			}
		}
		// 用于临时聊天时的图片发送
        else if(Const.CHAT_TYPE_GUEST$CHAT.equals(usedFor)) {
			// 插入一条聊天数据到界面等
			TChatDataHelper.addChatMessageData_outgoing(context, to, m);

			// 如果是用于消息转发，就直接跳过文件的处理过程，直接发消息（因为转发功能的使用前提是被转消息是正常收发状态的消息，即文件已经上传处理好了的）
			if(forForward) {
				// 并发送一条图片消息给好友
				TMessageHelper.sendImageMessageAsync((Activity) context, to, toName, processedFileName, fingerPring, null);
			}
		}
		// 用于群组聊天时的图片发送)
		else if(Const.CHAT_TYPE_GROUP$CHAT.equals(usedFor)) {
			// 插入一条聊天数据到界面等
			GChatDataHelper.addChatMessageData_outgoing(context, to, m);

			// 如果是用于消息转发，就直接跳过文件的处理过程，直接发消息（因为转发功能的使用前提是被转消息是正常收发状态的消息，即文件已经上传处理好了的）
			if(forForward) {
				// 并发送一条图片消息给好友
				GMessageHelper.sendImageMessageAsync((Activity) context, to, processedFileName, fingerPring, null);
			}
		}


		// 由于历史原因，rbp中图片和语音留言消息的特殊发送逻辑，无法做到在真正的send方法发送完成后进行调用，暂时就先放到这里执行，不太完美但也够用了
		if(sucessObsExtra != null) {
			sucessObsExtra.update(null, m);
		}
	}

    /**
     * 裁剪压缩并重命名。
     *
     * @param context
     * @param mImageDataType
     * @param mImageDataSrc
     * @param mBmOriginalForView
     * @param COMPRESS_QUALITY
     * @param notifyObserverAfterSucess
     * @return
     */
	public static boolean decreaseAndRenameSize(Context context, int mImageDataType
			, String mImageDataSrc, Bitmap mBmOriginalForView, int COMPRESS_QUALITY, Observer notifyObserverAfterSucess) {
		boolean sucess = false;

		if(mBmOriginalForView == null)
			return false;

		Bitmap decreasedBm = null;
		
		// ** 【说明：】因为在父类中将文件加载到Bitmap中时已经计算并调整了inSimpleSize值，因此本类中也就几乎没有必要来进行尺寸的处理了
//		// 【第一步：压缩尺寸】
//		// 当宽或高大于限定的最大尺寸时就按一定的比便进行尺寸压缩（压缩尺寸的目的是使得接收方查看图片时不致于因大图而消耗巨大内存）
//		if(bmOriginalForView.getWidth() > MAX_WIDTH_FOR_SEND 
//				|| bmOriginalForView.getHeight() > MAX_HEIGHT_FOR_SEND)
//		{
//			try
//			{
//				decreasedBm = BitmapHelper.decreaseBitmapSize(bmOriginalForView, DECREASE_PERCENT, false);
//				if(decreasedBm == null)
//				{
//					Log.e(TAG, "【SendPic】压缩尺寸完成后返回的Bitmap对象居然是null ？！");
//					return false;
//				}
//				else
//				{
//					Log.d(TAG, "【SendPic】尺寸压缩完成，压缩尺寸比例为："+DECREASE_PERCENT
//							+", [原"+bmOriginalForView.getWidth()+"*"+bmOriginalForView.getHeight()+"]" +
//									"|[现"+decreasedBm.getWidth()+"*"+decreasedBm.getHeight()+"].");
//				}
//			}
//			catch (OutOfMemoryError e)
//			{
//				WidgetUtils.showToast(this
//						, "内存不足，你可重启程序后再试！"//this.getString(R.string.user_info_avatar_upload_faild3)
//						, ToastType.WARN);
//				Log.e(TAG, "【SendPic】将原始压缩时内存溢出了！", e);
//			}
//
//		}

		// 【第二步：降低图片质量（从而减小文件大小以便节省网络传输数据量）】
		try {
			// 获取质量压缩后将要保存到的路径（目前的实现即是覆盖原始图片）
			File savedPath = null;
			if(mImageDataType == ImageDataType.FILE_PATH)
				savedPath = new File(mImageDataSrc);
			else if(mImageDataType == ImageDataType.URI)
				savedPath = UriToFileHelper.uri2File(context, Uri.parse(mImageDataSrc));
			else
				Log.w(TAG, "无效的imageDataType="+mImageDataType);

			if(savedPath != null) {
				boolean compressOk = BitmapHelper.saveBitmapToFile(
						// 如果之前压缩过尺寸则就以新尺寸的Bitmap对象进行压缩质量，否则使用原始Bitmap进行质量压缩并保存
						decreasedBm == null?mBmOriginalForView:decreasedBm, COMPRESS_QUALITY, savedPath);
				if(compressOk) {
					Log.d(TAG, "【SendPic】质量压缩完成，压缩质量为："+COMPRESS_QUALITY+", 临时文件保存路径是："+savedPath);
					
					boolean renameOk = renameUseMD5(context, savedPath, notifyObserverAfterSucess);
					if(renameOk) {
						Log.d(TAG, "【SendPic】要发送的图片重命名完成.");
						// 
						sucess = true;
					} else {
						Log.e(TAG, "【SendPic】要发送的图片重命名失败！");
					}
				} else {
					Log.w(TAG, "【SendPic】质量压缩失败！！！压缩质量为：" + COMPRESS_QUALITY + ", 将要保存路径是：" + savedPath);
				}
			} else {
				Log.e(TAG, "【SendPic】质量压缩时，压缩完成后将要保存的路径居然是null ？！savedPath="+savedPath);
			}
		} catch (Exception e) {
			Log.e(TAG, "【SendPic】降低图片质量的过程中出错了！", e);
		}

		return sucess;
	}

    /**
     * 使用该图片的md5码重命名图片文件名。
     *
     * @param context
     * @param tempFileSavedPath
     * @param notifyObserverAfterSucess
     * @return
     */
	private static boolean renameUseMD5(Context context, File tempFileSavedPath, Observer notifyObserverAfterSucess) {
		boolean sucess = false;
		// 计算出头像文件的MD5码
		byte[] fileData = null;
		try {
			// 将要上传的图像文件数据读取出来
			fileData = FileHelper.readFileWithBytes(tempFileSavedPath);

			// 读取出来的数据是空的，上传当然就没有必要继续了
			if(fileData == null)
				return false;

			// 计算图片文件的MD5码
			final String fileMd5 = getTempFileMD5(fileData);
			if(fileMd5 != null) {
				// 本户存放于数据库、本地缓存的文件名格式是："MD5码.jpg",形如:"0b272fca28252641231a94f63d8e25fa.jpg"
				String fileNameUsedMd5 = SendImageHelper.constructImageFileName(fileMd5);
				
				File fileAfterRename = null;
				try {
					//
					fileAfterRename = new File(tempFileSavedPath.getParent()+"/"+fileNameUsedMd5);
					tempFileSavedPath.renameTo(fileAfterRename);
					
					// 通知观察者
					if(notifyObserverAfterSucess != null)
						notifyObserverAfterSucess.update(null, fileAfterRename.getName()); // 重命名后的文件名作为data返回
//					//
//					processedFileName = fileAfterRename.getName();
					sucess = true;
				} catch (Exception e) {
					Log.e(TAG, "【SendPic】将临时文件"+tempFileSavedPath+"重命名失败了，上传将不能继续！", e);
					// 如果重命名失败，就尝试把这个文件删除（没有必要留了）
					try{
						fileAfterRename.delete();
					} catch (Exception e2){
					}
				}
			}
		}
		// 显示处理下OOM使得APP更健壮（OOM只能显示抓取，否则会按系统Error的方式报出从而使APP崩溃哦）
		catch (OutOfMemoryError e) {
			WidgetUtils.showToast(context, context.getString(R.string.chat_sendpic_preview_and_send_oom), ToastType.WARN);
			Log.e(TAG, "【SendPic】将图片文件数据读取到内存时内存溢出了，上传没有继续！", e);
		} catch (Exception e) {
			Log.e(TAG, "【SendPic】尝试将图片临时文件数据读取出来时出错了，"+e.getMessage()+"，上传将不能继续！", e);
		}

		return sucess;
	}

    /**
     * 计算md5码。
     *
     * @param fileData
     * @return
     */
	private static String getTempFileMD5(byte[] fileData) {
		try {
			return FileHelper.getFileMD5(fileData);
		} catch (Exception e) {
			Log.w(TAG, "【SendPic】计算MD5码时出错了，"+e.getMessage(), e);
			return null;
		}
	}
}
